package org.example.service.impl;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.example.dao.OrderDOMapper;
import org.example.dao.SequenceDOMapper;
import org.example.dataobject.OrderDO;
import org.example.dataobject.SequenceDO;
import org.example.error.BusinessException;
import org.example.error.EmBusinessError;
import org.example.service.ItemService;
import org.example.service.OrderService;
import org.example.service.UserService;
import org.example.service.model.ItemModel;
import org.example.service.model.OrderModel;
import org.example.service.model.UserModel;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * @Author itmei
 * @Package org.example.service.impl
 * @Date 2025/6/2 19:52
 */
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private ItemService itemService;
    @Autowired
    private UserService userService;
    @Autowired
    private OrderDOMapper orderDOMapper;
    @Autowired
    private SequenceDOMapper sequenceDOMapper;
    @Autowired
    private RedissonClient redissonClient;


    private static final int tableCount = 100; // 固定分100张表
    @Override
    @Transactional
    @SentinelResource(value = "createOrder",blockHandler = "handleCreateOrderBlock")//sentinel限流
    public OrderModel createOrder(Integer userId, Integer itemId, Integer promoId, Integer amount) throws BusinessException {
        //校验下单状态，下单的商品是否存在，用户是否合法，购买数量是否正确
        ItemModel itemModel=itemService.getItemById(itemId);
        if(itemModel==null){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"商品信息不存在");
        }
        UserModel userModel=userService.getUserById(userId);
        if(userModel==null){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"用户信息不存在");
        }
        if(amount<=0||amount>99){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"数量信息不正确");
        }
        if (promoId != null && promoId.intValue() == 0) {
            promoId = null;
        }
        if (promoId != null) {
            // 1. 先检查商品是否关联了活动（避免空指针）
            if (itemModel.getPromoModel() == null) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "商品未关联任何活动");
            }
            // 2. 检查传入的 promoId 是否与商品关联的活动 ID 一致
            if (promoId.intValue() != itemModel.getPromoModel().getId()) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "活动与商品不匹配");
            }
            // 3. 检查活动状态是否有效（假设 2 代表“活动中”）
            if (itemModel.getPromoModel().getStatus() != 2) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "活动未开始或已结束");
            }
        }
        //创建redission分布式锁，key为商品id
        String lockKey="stock:lock:"+itemId;
        RLock lock=redissonClient.getLock(lockKey);
        boolean lockAcquired = false;
        //下单减库存，支付减库存
        try {
            //尝试获取分布式锁，等待3秒，超时20秒后释放锁，避免死锁。检查库存->创建订单->更新库存。
            lockAcquired = lock.tryLock(3, 20, TimeUnit.SECONDS);
            if (!lockAcquired) {
                throw new BusinessException(EmBusinessError.ORDER_CREATE_FAIL, "系统繁忙，请稍后重试");
            }
            //检查库存是否充足
            boolean result=itemService.decreaseStock(itemId,amount);
            if(!result){
                throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH,EmBusinessError.STOCK_NOT_ENOUGH.getErrMsg());
            }
            //订单入库
            OrderModel orderModel=new OrderModel();
            orderModel.setUserId(userId);
            orderModel.setItemId(itemId);
            orderModel.setAmount(amount);
            if(promoId!=null){
                orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice());
            }else {
                orderModel.setItemPrice(itemModel.getPrice());
            }

            orderModel.setPromoId(promoId);
            orderModel.setOrderPrice(orderModel.getItemPrice().multiply(new BigDecimal(amount)));
            //生成交易流水号
            orderModel.setId(generateOrderNo(userId));
            OrderDO orderDO=converFromOrderModel(orderModel);
            orderDOMapper.insertSelective(orderDO);
            itemService.increaseSales(itemId,amount);

            //返回前端
            return orderModel;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("createOrder中断异常：");
            e.printStackTrace(); // 强制打印堆栈
            throw new BusinessException(EmBusinessError.ORDER_CREATE_FAIL, "订单创建被中断");
        } finally {
            // 7. 释放锁（仅释放当前线程持有的锁）
            if (lockAcquired && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    /**
     * //加上这个注解就不怕@Transactional注解，
     *     处理事物中代码出错导致sequence也一直被使用无法更新，
     *     REQUIRES_NEW无论目前代码是否在事物中，都会重新开启新的事物*/
    public String generateOrderNo(Integer userId) throws BusinessException {
        int maxRetries = 8;

        for (int retryCount = 0; retryCount < maxRetries; retryCount++) {
            try {
                return attemptGenerateOrderNo(userId);
            } catch (DataAccessException e) { // 父类异常包含子类
                if (retryCount == maxRetries - 1) {
                    throw new BusinessException(EmBusinessError.ORDER_GENERATE_FAIL, "订单号生成失败，重试次数耗尽");
                }
                sleepWithExponentialBackoff(retryCount + 1);
            }
        }
        throw new BusinessException(EmBusinessError.ORDER_GENERATE_FAIL, "订单号生成失败");
    }

    /**
     * 尝试生成订单号（核心逻辑）
     */
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public String attemptGenerateOrderNo(Integer userId) {
       /** 获取当前sequnence，有一个存在问题是：sequnence没有设置一个最大值，当我的数值一直加大可能会超过六位，应该设置一个最大值在sequence
        CurrentValue比最大值还大的时候，应返回到初始值initialvalue，所以可以在sequence中加入两个字段，
        最大MaxCurrentValue和InitialCurrentvalue,下面代码以及解决。用乐观锁
        * */
        int maxSequenceValue = 999999; // 6位最大值
        int initialSequenceValue = 1;
        // 1. 获取当前日期部分 (YYYYMMDD)
        String nowDate = LocalDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE);

        // 2. 获取序列信息
        SequenceDO sequenceDO = sequenceDOMapper.getSequenceByName("order_info");
        if (sequenceDO == null) {
            throw new RuntimeException("序列配置不存在");
        }

        // 3. 记录当前序列值（用于本次订单号）
        int currentValue = sequenceDO.getCurrentValue();

        // 4. 计算新序列值
        int newValue = currentValue + sequenceDO.getStep();

        // 5. 检查并重置序列值
        if (newValue > maxSequenceValue) {
            sequenceDO.setCurrentValue(initialSequenceValue); // 直接修改为初始值
            newValue = initialSequenceValue;
        }

        // 6. 准备更新序列
        sequenceDO.setCurrentValue(newValue);


        // 7. 乐观锁更新
        int updatedRows = sequenceDOMapper.updateSequenceWithOptimisticLock(sequenceDO);
        if (updatedRows == 0) {
            // 触发重试
            throw new OptimisticLockingFailureException("序列更新冲突");
        }

        // 8. 生成订单号
        String sequenceStr = String.format("%06d", currentValue); // 6位序列号
        // 4. 动态计算分表位
        int shardId = userId % tableCount;                             // 取模分表
        String shardBit = String.format("%02d", shardId);             // 2位分表位

        return nowDate + sequenceStr + shardBit; // 16位订单号
    }


    private OrderDO converFromOrderModel(OrderModel orderModel){
        if(orderModel==null){
            return null;
        }
        OrderDO orderDO=new OrderDO();
        BeanUtils.copyProperties(orderModel,orderDO);
        orderDO.setItemPrice(orderModel.getItemPrice().doubleValue());
        orderDO.setOrderPrice(orderModel.getOrderPrice().doubleValue());
        return orderDO;
    }
    private void sleepWithExponentialBackoff(int retryCount) {
        long maxWaitTime = 2000;//最大等待时间2秒
        try {
            long sleepTime = (long) Math.pow(2, retryCount) * 50; // 50, 100, 200ms...
            long jitter = ThreadLocalRandom.current().nextLong(0,sleepTime/2);
            long totalWaitTime = Math.min(sleepTime+jitter,maxWaitTime);
            Thread.sleep(totalWaitTime);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    public OrderModel handleCreateOrderBlock(Integer userId, Integer itemId, Integer promoId, Integer amount) throws BusinessException {
        throw new BusinessException(EmBusinessError.RATE_LIMIT_EXCEEDED,EmBusinessError.RATE_LIMIT_EXCEEDED.getErrMsg());
    }
}
